﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Gmantis.Controls.Automation;

namespace Gmantis.Controls.TileView
{
    public class TileViewItem : HeaderedContentControl
    {
        // Fields
        internal FrameworkElement _elementHeader;
        internal ContentPresenter _elementPresenter;
        internal ButtonBase _elementStateButton;
        internal bool _isLoaded;
        private AutomationPeer _peer;
        public static readonly DependencyProperty ContentMaximizedProperty;
        public static readonly DependencyProperty ContentMinimizedProperty;
        public static readonly DependencyProperty ContentTemplateMaximizedProperty;
        public static readonly DependencyProperty ContentTemplateMaximizedSelectorProperty;
        public static readonly DependencyProperty ContentTemplateMinimizedProperty;
        public static readonly DependencyProperty ContentTemplateMinimizedSelectorProperty;
        public static readonly DependencyProperty ContentTemplateSelectorProperty;
        public static readonly DependencyProperty FocusBrushProperty;
        internal const string HeaderElementName = "Header";
        internal MouseHelper headerMouseHelper;
        public static readonly DependencyProperty HeaderTemplateSelectorProperty;
        internal bool isPrepared;
        internal Rect? LastFinalRect;
        public static readonly DependencyProperty MouseOverBrushProperty;
        internal double Offset;
        internal const string PresenterElementName = "Presenter";
        public static readonly DependencyProperty PressedBrushProperty;
        internal const string StateButtonElementName = "StateButton";
        public static readonly DependencyProperty TiledStateProperty;
        internal TileView TileView;

        // Methods
        static TileViewItem()
        {
            HeaderTemplateSelectorProperty = DependencyProperty.Register("HeaderTemplateSelector", typeof(DataTemplateSelector), typeof(TileViewItem), new PropertyMetadata(null));
            ContentTemplateSelectorProperty = DependencyProperty.Register("ContentTemplateSelector", typeof(DataTemplateSelector), typeof(TileViewItem), new PropertyMetadata(null));
            ContentTemplateMaximizedSelectorProperty = DependencyProperty.Register("ContentTemplateMaximizedSelector", typeof(DataTemplateSelector), typeof(TileViewItem), new PropertyMetadata(null));
            ContentTemplateMinimizedSelectorProperty = DependencyProperty.Register("ContentTemplateMinimizedSelector", typeof(DataTemplateSelector), typeof(TileViewItem), new PropertyMetadata(null));
            TiledStateProperty = DependencyProperty.Register("TiledState", typeof(TiledState), typeof(TileViewItem), new PropertyMetadata(TiledState.Tiled, new PropertyChangedCallback(TileViewItem.OnTiledStatePropertyChanged)));
            ContentMaximizedProperty = DependencyProperty.Register("ContentMaximized", typeof(object), typeof(TileViewItem), new PropertyMetadata(new PropertyChangedCallback(TileViewItem.OnContentMaximizedPropertyChanged)));
            ContentMinimizedProperty = DependencyProperty.Register("ContentMinimized", typeof(object), typeof(TileViewItem), new PropertyMetadata(string.Empty, new PropertyChangedCallback(TileViewItem.OnContentMinimizedPropertyChanged)));
            ContentTemplateMaximizedProperty = DependencyProperty.Register("ContentTemplateMaximized", typeof(DataTemplate), typeof(TileViewItem), new PropertyMetadata(new PropertyChangedCallback(TileViewItem.OnContentTemplateMaximizedPropertyChanged)));
            ContentTemplateMinimizedProperty = DependencyProperty.Register("ContentTemplateMinimized", typeof(DataTemplate), typeof(TileViewItem), new PropertyMetadata(new PropertyChangedCallback(TileViewItem.OnContentTemplateMinimizedPropertyChanged)));
            MouseOverBrushProperty = DependencyProperty.Register("MouseOverBrush", typeof(Brush), typeof(TileViewItem), null);
            PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(TileViewItem), null);
            FocusBrushProperty = DependencyProperty.Register("FocusBrush", typeof(Brush), typeof(TileViewItem), null);
        }

        public TileViewItem()
        {
            RoutedEventHandler handler = null;
            base.DefaultStyleKey = typeof(TileViewItem);
            if (handler == null)
            {
                handler = (param0, param1) => this.ChangeVisualStateTiled(false);
            }
            base.Loaded += handler;
            this.SetCustomDefaultValues();
        }

        protected void ChangeVisualStateTiled(bool useTransitions)
        {
            if (this.TiledState == TiledState.Minimized)
            {
                VisualStateHelper.GoToState(this, "Minimized", useTransitions);
            }
            if (this.TiledState == TiledState.Maximized)
            {
                VisualStateHelper.GoToState(this, "Maximized", useTransitions);
            }
            if (this.TiledState == TiledState.Tiled)
            {
                VisualStateHelper.GoToState(this, "Tiled", useTransitions);
            }
        }

        private IList GetListForUpdate()
        {
            IList itemsSource = null;
            if (!this.TileView.UpdateSourceCollection)
            {
                return null;
            }
            if (this.TileView.ItemsSource == null)
            {
                return this.TileView.Items;
            }
            itemsSource = this.TileView.ItemsSource as IList;
            if (itemsSource != null)
            {
                if (!(itemsSource is INotifyCollectionChanged))
                {
                    return null;
                }
                if (itemsSource.IsFixedSize)
                {
                    return null;
                }
                if (itemsSource.IsReadOnly)
                {
                    itemsSource = null;
                }
            }
            return itemsSource;
        }

        private T GetTemplateChild<T>(string childName, bool required, ref string errors) where T : class
        {
            DependencyObject templateChild = base.GetTemplateChild(childName);
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "template part", childName, templateChild, required, ref errors);
            return (templateChild as T);
        }

        private static Storyboard GetTemplateChildResource(FrameworkElement root, string resourceName, bool required, ref string errors)
        {
            return GetTemplateChildResource<Storyboard>(root, resourceName, required, ref errors);
        }

        private static T GetTemplateChildResource<T>(FrameworkElement root, string resourceName, bool required, ref string errors) where T : class
        {
            object child = root.Resources[resourceName];
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "resource", resourceName, child, required, ref errors);
            return (child as T);
        }

        private void InitializeHeaderPart()
        {
            this.headerMouseHelper = new MouseHelper(this._elementHeader);
            this.headerMouseHelper.MouseDoubleClick += delegate(object param0, MouseButtonEventArgs param1)
            {
                if (this.TiledState == TiledState.Maximized)
                {
                    this.TiledState = TiledState.Tiled;
                }
                else
                {
                    this.TiledState = TiledState.Maximized;
                }
            };
        }

        private void InitializeStateButtonPart()
        {
            this._elementStateButton.Click += delegate(object param0, System.Windows.RoutedEventArgs param1)
            {
                if (this.TiledState == TiledState.Maximized)
                {
                    this.TiledState = TiledState.Tiled;
                }
                else
                {
                    this.TiledState = TiledState.Maximized;
                }
            };
        }

        private void OnAfterApplyTemplate()
        {
            this.UpdatePresenter();
        }

        public override void OnApplyTemplate()
        {
            string errors = string.Empty;
            base.OnApplyTemplate();
            this._isLoaded = true;
            this._elementPresenter = this.GetTemplateChild<ContentPresenter>("Presenter", true, ref errors);
            this._elementStateButton = this.GetTemplateChild<ButtonBase>("StateButton", false, ref errors);
            if (this._elementStateButton != null)
            {
                this.InitializeStateButtonPart();
            }
            this._elementHeader = this.GetTemplateChild<FrameworkElement>("Header", false, ref errors);
            if (this._elementHeader != null)
            {
                this.InitializeHeaderPart();
            }
            if (!string.IsNullOrEmpty(errors))
            {
                this._isLoaded = false;
                if ((base.Template != null) && !PlatformIndependent.IsInDesignMode(this))
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Template cannot be applied to TileViewItem.\nDetails: {0}", new object[] { errors }));
                }
            }
            else
            {
                this.ChangeVisualStateTiled(false);
                this.OnAfterApplyTemplate();
            }
        }

        private void OnContentMaximizedChanged(object oldValue)
        {
            this.UpdatePresenter();
        }

        private static void OnContentMaximizedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TileViewItem item = d as TileViewItem;
            object oldValue = e.OldValue;
            item.OnContentMaximizedChanged(oldValue);
        }

        private void OnContentMinimizedChanged(object oldValue)
        {
            this.UpdatePresenter();
        }

        private static void OnContentMinimizedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TileViewItem item = d as TileViewItem;
            object oldValue = e.OldValue;
            item.OnContentMinimizedChanged(oldValue);
        }

        private void OnContentTemplateMaximizedChanged(DataTemplate oldValue)
        {
            this.UpdatePresenter();
        }

        private static void OnContentTemplateMaximizedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TileViewItem item = d as TileViewItem;
            DataTemplate oldValue = (DataTemplate)e.OldValue;
            item.OnContentTemplateMaximizedChanged(oldValue);
        }

        private void OnContentTemplateMinimizedChanged(DataTemplate oldValue)
        {
            this.UpdatePresenter();
        }

        private static void OnContentTemplateMinimizedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TileViewItem item = d as TileViewItem;
            DataTemplate oldValue = (DataTemplate)e.OldValue;
            item.OnContentTemplateMinimizedChanged(oldValue);
        }

        protected override AutomationPeer OnCreateAutomationPeer()
        {
            if (this._peer == null)
            {
                this._peer = AutomationPeerFactory.CreateAutomationPeer(this, typeof(TileViewItem));
            }
            if (this._peer == null)
            {
                return base.OnCreateAutomationPeer();
            }
            return this._peer;
        }

        private void OnHeaderChanged(object oldValue)
        {
        }

        private void OnTiledStateChanged(object oldValue)
        {
            if (this.TileView != null)
            {
                object obj2 = this.TileView.ItemContainerGenerator.ItemFromContainer(this);
                if (this.TileView.Items.Contains(obj2))
                {
                    switch (this.TiledState)
                    {
                        case TiledState.Tiled:
                            this.TileView.MaximizedItem = null;
                            break;

                        case TiledState.Maximized:
                            this.TileView.MaximizedItem = obj2;
                            break;

                        case TiledState.Minimized:
                            if ((this.TileView.MaximizedItem == obj2) || (this.TileView.MaximizedItem == null))
                            {
                                this.TileView.MaximizedItem = null;
                            }
                            break;
                    }
                    this.UpdatePresenter();
                    if (this.TileView.Panel != null)
                    {
                        this.TileView.Panel.InvalidateMeasure();
                    }
                }
            }
        }

        private static void OnTiledStatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TileViewItem item = d as TileViewItem;
            TiledState oldValue = (TiledState)e.OldValue;
            item.OnTiledStateChanged(oldValue);
            item.ChangeVisualStateTiled(true);
        }

        private void SetCustomDefaultValues()
        {
            Binding binding = new Binding("FontFamily")
            {
                Source = this
            };
            base.SetBinding(HeaderedContentControl.HeaderFontFamilyProperty, binding);
        }

        internal void TryReCaptureMouse()
        {
            if (((this.headerMouseHelper != null) && (this.TileView != null)) && (this.TileView.GrabbedIndex == this.TileView.IndexFromTileViewItem(this)))
            {
                this.TileView.ReleaseMouseCapture();
                this.LastFinalRect = this.TileView.GrabbedLastRect;
            }
        }

        internal void UpdatePresenter()
        {
            if (this._elementPresenter != null)
            {
                object content = null;
                DataTemplate contentTemplate = null;
                switch (this.TiledState)
                {
                    case TiledState.Tiled:
                        content = base.Content;
                        contentTemplate = base.ContentTemplate;
                        break;

                    case TiledState.Maximized:
                        content = this.ContentMaximized ?? base.Content;
                        contentTemplate = this.ContentTemplateMaximized ?? base.ContentTemplate;
                        break;

                    case TiledState.Minimized:
                        content = this.ContentMinimized ?? base.Content;
                        contentTemplate = this.ContentTemplateMinimized ?? base.ContentTemplate;
                        break;
                }
                if (this._elementPresenter.Content != content)
                {
                    this._elementPresenter.Content = content;
                }
                if (this._elementPresenter.ContentTemplate != contentTemplate)
                {
                    this._elementPresenter.ContentTemplate = contentTemplate;
                }
            }
        }

        // Properties
        [TypeConverter(typeof(StringToObjectConverter))]
        public object ContentMaximized
        {
            get { return GetValue(ContentMaximizedProperty); }
            set { SetValue(ContentMaximizedProperty, value); }
        }

        [TypeConverter(typeof(StringToObjectConverter))]
        public object ContentMinimized
        {
            get { return GetValue(ContentMinimizedProperty); }
            set { SetValue(ContentMinimizedProperty, value); }
        }

        public DataTemplate ContentTemplateMaximized
        {
            get { return (DataTemplate)GetValue(ContentTemplateMaximizedProperty); }
            set { SetValue(ContentTemplateMaximizedProperty, value); }
        }

        public DataTemplateSelector ContentTemplateMaximizedSelector
        {
            get { return (DataTemplateSelector)GetValue(ContentTemplateMaximizedSelectorProperty); }
            set { SetValue(ContentTemplateMaximizedSelectorProperty, value); }
        }

        public DataTemplate ContentTemplateMinimized
        {
            get { return (DataTemplate)GetValue(ContentTemplateMinimizedProperty); }
            set { SetValue(ContentTemplateMinimizedProperty, value); }
        }

        public DataTemplateSelector ContentTemplateMinimizedSelector
        {
            get { return (DataTemplateSelector)GetValue(ContentTemplateMinimizedSelectorProperty); }
            set { SetValue(ContentTemplateMinimizedSelectorProperty, value); }
        }

        public DataTemplateSelector ContentTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(ContentTemplateSelectorProperty); }
            set { SetValue(ContentTemplateSelectorProperty, value); }
        }

        public Brush FocusBrush
        {
            get { return (Brush)GetValue(FocusBrushProperty); }
            set { SetValue(FocusBrushProperty, value); }
        }

        public DataTemplateSelector HeaderTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(HeaderTemplateSelectorProperty); }
            set { SetValue(HeaderTemplateSelectorProperty, value); }
        }

        public Brush MouseOverBrush
        {
            get { return (Brush)GetValue(MouseOverBrushProperty); }
            set { SetValue(MouseOverBrushProperty, value); }
        }

        public Brush PressedBrush
        {
            get { return (Brush)GetValue(PressedBrushProperty); }
            set { SetValue(PressedBrushProperty, value); }
        }

        public TiledState TiledState
        {
            get { return (TiledState)GetValue(TiledStateProperty); }
            set { SetValue(TiledStateProperty, value); }
        }
    }

    public enum TiledState
    {
        Tiled,
        Maximized,
        Minimized
    }
}
